/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/
/* 
1.Promise是个对象，执行时需要传一个执行器
2.3种状态pending、fulfilled、rejected
3.resolve、reject更改状态
4.then方法 内部判断状态 成功状态就调用成功回调，失败状态就调用失败回调，
5.then 回调有一个参数 成功的值 失败有失败的原因
*/

const { values } = require('lodash')

//  等待
const PENDING = 'pending'
// 成功
const FULFILLED = 'fulfilled'
// 失败
const REJECTED = 'rejected'

function resolvePromise(promise, x, resolve, reject) {
  if (promise === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  if (x instanceof MyPromise) {
    // promise
    x.then(resolve, reject)
  } else {
    // 普通值
    resolve(x)
  }
}
class MyPromise {
  constructor(exector) {
    try {
      exector(this.resolve, this.reject)
    } catch (error) {
      this.reject(e)
    }
  }
  // Promise 状态
  status = PENDING
  // 成功的值
  value = undefined
  // 失败的原因
  reason = undefined
  // 回调
  successCallbacks = []
  failCallbacks = []

  resolve = (value) => {
    // 如果状态不是等待 阻止继续执行 返回
    if (this.status !== PENDING) return

    // 将状态改为成功
    this.status = FULFILLED
    // 保存成功之后的值
    this.value = value
    // 判断成功回调是否存在 如果存在 调用
    // this.successCallbacks && this.successCallbacks(this.value)
    // 循环执行所有成功回调
    // console.log(this.successCallbacks.shift())
    while (this.successCallbacks.length) {
      this.successCallbacks.shift()()
    }
  }

  reject = (reason) => {
    // 如果状态不是等待 返回
    if (this.status !== PENDING) return
    // 将状态改为失败
    this.status = REJECTED
    // if (!this.failCallbacks.length) {
    //   throw '(in MyPromise)'
    // }
    // 保存失败之后的原因
    this.reason = reason
    // 判断失败回调是否存在 如果存在 调用
    // this.failCallbacks && this.failCallbacks(this.reason)

    // 循环执行所有失败回调
    while (this.failCallbacks.length) {
      this.failCallbacks.shift()()
    }
  }

  then = (successCallback, failCallback) => {
    // 可选参数
    successCallback = successCallback ? successCallback : (value) => value
    failCallback = failCallback
      ? failCallback
      : (reason) => {
          throw reason
        }
    let promise2 = new MyPromise((resolve, reject) => {
      // 判断状态
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            let x = successCallback(this.value)
            // 判断x 是普通值还是promise对象
            // 普通值 直接resolve
            // promise对象 查看promise 对象返回的结果
            // 再根据promise对象返回的结果 决定调用resolve还是调用reject
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      } else if (this.status === REJECTED) {
        failCallback(this.reason)
      } else {
        // 等待
        // 保存成功回调和失败回调保存
        this.successCallbacks.push(() =>
          setTimeout(() => {
            try {
              let x = successCallback(this.value)
              // 判断x 是普通值还是promise对象
              // 普通值 直接resolve
              // promise对象 查看promise 对象返回的结果
              // 再根据promise对象返回的结果 决定调用resolve还是调用reject
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        )
        this.failCallbacks.push(() =>
          setTimeout(() => {
            try {
              let x = successCallback(this.value)
              // 判断x 是普通值还是promise对象
              // 普通值 直接resolve
              // promise对象 查看promise 对象返回的结果
              // 再根据promise对象返回的结果 决定调用resolve还是调用reject
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        )
      }
    })
    return promise2
  }

  finally(callbak) {
    return this.then(
      (value) => {
        return MyPromise.resolve(callbak().then(() => value))
      },
      (reason) => {
        return MyPromise.resolve(
          callbak().then(() => {
            throw reason
          })
        )
      }
    )
  }

  catch(failCallback) {
    return this.then(undefined, failCallback)
  }
  /* 
  接受数组, 返回值Promise对象
  */
  static all = (array) => {
    // 结果
    let result = []
    let index = 0
    return new Promise((resolve, reject) => {
      function addData(key, value) {
        result[key] = value
        index++
        if (index === array.length) {
          resolve(result)
        }
      }
      for (let i = 0; i < array.length; i++) {
        let current = array[i]
        if (current instanceof myPromise) {
          // promise对象
          current.then(
            (value) => {
              addData(i, value)
            },
            (reason) => {
              reject(reason)
            }
          )
        } else {
          // 普通值
          addData(i, array[i])
        }
      }
      resolve(result)
    })
  }

  static resolve(value) {
    if (value instanceof MyPromise) return value
    return new MyPromise((resolve) => resolve(value))
  }
}

module.exports = MyPromise
